package dianping;

import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import com.opencsv.exceptions.CsvValidationException;

import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

public class DataCleaner {

    private static final String IN_REST  = "/restaurants.csv";
    private static final String IN_RATE  = "/ratings.csv";
    private static final String OUT_FILE = "target/dianping_clean.csv";

    /* ---------- 数据模型 ---------- */
    public static class Restaurant {
        public final String restId;
        public final String name;
        public Restaurant(String restId, String name) {
            this.restId = restId;
            this.name   = name.trim(); // 不再处理空值，因为构造函数外已经过滤
        }
    }

    public static class Rating {
        public final String userId;
        public final String restId;
        public final double overall;
        public final double env;
        public final double flavor;
        public final double service;
        public final long   timestamp;
        public final String comment;

        public Rating(String userId, String restId,
                      double overall, double env,
                      double flavor, double service,
                      long timestamp, String comment) {
            this.userId    = userId;
            this.restId    = restId;
            this.overall   = overall;
            this.env       = env;
            this.flavor    = flavor;
            this.service   = service;
            this.timestamp = timestamp;
            this.comment   = comment == null ? "" : comment.trim();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Rating)) return false;
            Rating r = (Rating) o;
            return timestamp == r.timestamp &&
                    userId.equals(r.userId) &&
                    restId.equals(r.restId);
        }
        @Override
        public int hashCode() {
            return Objects.hash(userId, restId, timestamp);
        }
    }

    public static void main(String[] args) {
        try {
            Map<String, Restaurant> restMap = loadRestaurants();
            List<Rating> ratings = loadRatings(restMap);

            writeMerged(ratings, restMap);

            System.out.println("清洗完成，输出文件：" + Paths.get(OUT_FILE).toAbsolutePath());
            System.out.printf("有效餐厅数量=%d, 有效评价数量=%d%n",
                    restMap.size(), ratings.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /* ---------- 读 restaurants ---------- */
    private static Map<String, Restaurant> loadRestaurants() throws IOException, CsvValidationException {
        try (InputStream in = DataCleaner.class.getResourceAsStream(IN_REST);
             CSVReader reader = new CSVReader(new InputStreamReader(
                     Objects.requireNonNull(in, "restaurants.csv not found")))) {

            reader.readNext(); // skip header
            Map<String, Restaurant> map = new LinkedHashMap<>();
            String[] line;
            while ((line = reader.readNext()) != null) {
                if (line.length < 2) continue;
                String id = line[0] == null ? "" : line[0].trim();
                String name = line.length > 1 ? (line[1] == null ? "" : line[1].trim()) : "";

                // 严格过滤：ID和name都不能为空
                if (!id.isEmpty() && !name.isEmpty()) {
                    map.put(id, new Restaurant(id, name));
                }
            }
            return map;
        }
    }

    /* ---------- 读 ratings ---------- */
    private static List<Rating> loadRatings(Map<String, Restaurant> restMap) throws IOException, CsvValidationException {
        try (InputStream in = DataCleaner.class.getResourceAsStream(IN_RATE);
             CSVReader reader = new CSVReader(new InputStreamReader(
                     Objects.requireNonNull(in, "ratings.csv not found")))) {

            reader.readNext(); // skip header
            Set<Rating> unique = new LinkedHashSet<>();
            String[] line;
            while ((line = reader.readNext()) != null) {
                if (line.length < 8) continue;

                String userId = line[0] == null ? "" : line[0].trim();
                String restId = line[1] == null ? "" : line[1].trim();

                // 处理评分数据
                Double overall = parseDouble(line[2]);
                if (overall == null) {
                    Double env = parseDouble(line[3]);
                    Double flavor = parseDouble(line[4]);
                    Double service = parseDouble(line[5]);

                    if (env != null && flavor != null && service != null) {
                        overall = (env + flavor + service) / 3.0;
                    } else {
                        continue;
                    }
                }

                Double env = parseDouble(line[3]);
                if (env == null) env = overall;

                Double flavor = parseDouble(line[4]);
                if (flavor == null) flavor = overall;

                Double service = parseDouble(line[5]);
                if (service == null) service = overall;

                Long ts = parseLong(line[6]);
                String comment = line.length > 7 ? (line[7] == null ? "" : line[7].trim()) : "";

                /* 关键字段验证 */
                if (userId.isEmpty() || restId.isEmpty() || ts == null || !restMap.containsKey(restId)) {
                    continue;
                }

                unique.add(new Rating(userId, restId, overall, env, flavor, service, ts, comment));
            }
            return new ArrayList<>(unique);
        }
    }

    /* ---------- 写出合并文件 ---------- */
    private static void writeMerged(List<Rating> ratings,
                                    Map<String, Restaurant> restMap) throws IOException {
        Files.createDirectories(Paths.get(OUT_FILE).getParent());
        try (Writer w = Files.newBufferedWriter(Paths.get(OUT_FILE));
             CSVWriter writer = new CSVWriter(w)) {

            writer.writeNext(new String[]{
                    "userId","restId","name",
                    "rating","rating_env","rating_flavor","rating_service",
                    "timestamp","comment"
            });

            for (Rating r : ratings) {
                Restaurant re = restMap.get(r.restId);
                writer.writeNext(new String[]{
                        r.userId,
                        r.restId,
                        re.name,
                        String.format("%.1f", r.overall),
                        String.format("%.1f", r.env),
                        String.format("%.1f", r.flavor),
                        String.format("%.1f", r.service),
                        String.valueOf(r.timestamp),
                        r.comment
                });
            }
        }
    }

    /* ---------- 工具方法 ---------- */
    private static Double parseDouble(String s) {
        if (s == null || s.trim().isEmpty()) return null;
        try { return Double.parseDouble(s.trim()); }
        catch (NumberFormatException e) { return null; }
    }

    private static Long parseLong(String s) {
        if (s == null || s.trim().isEmpty()) return null;
        try { return Long.parseLong(s.trim()); }
        catch (NumberFormatException e) { return null; }
    }
}